package org.example.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.example.config.ApiConfig;
import org.example.entity.requestbody.KeyFilter;
import org.example.entity.utilEntity.ListPage;
import org.example.util.*;
import org.example.util.constantUtil.ConstantUtil;
import org.example.util.DeviceUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.ClassPathResource;
import org.springframework.stereotype.Service;

import java.io.InputStream;
import java.util.*;
import java.util.concurrent.*;

import static org.example.enums.ResultCode.*;

@Service
@Slf4j
public class FrameServiceImpl {
    
    @Autowired
    private DeviceService deviceService;
    @Autowired
    private ApiConfig apiConfig;
    @Autowired
    private TimeseriesService timeseriesService;
    // 获取租户设备URL
    private static final String TYPE=ConstantUtil.TYPE;

    public Result getLatestTimeseriesWithTitleAndTs(String imei,String token,String _uuid){
        // 1. 获取B1帧和A1帧数据
        Result resultDevice=getLatestTimeseries(imei,token,_uuid);  // 通过uuid查询设备最新数据
        if(!resultDevice.getSuccess()){
            return resultDevice;
        }
        // 2.添加customerTitle、uuid和createdTs
        String uuid=null;
        if(_uuid!=null){
            uuid=_uuid;
        } else {
            uuid=DeviceUtil.SNtoUUID.get(imei);
        }
        Map<String,Object> resultDeviceMap=(Map<String,Object>)resultDevice.getData();  // 不管成功与否都是返回resultMap
        resultDeviceMap.put("uuid",uuid);
        resultDeviceMap.put("createdTime",DeviceUtil.ImeiCreatedTime.get(imei));
        resultDeviceMap.put("customerTitle", DeviceUtil.ImeiCustomerTitle.get(imei));

        return resultDevice;
    }

    /**
     * 按imei或uuid查询设备信息
     * @param imei 设备imei号
     * @param token 鉴权token
     * @param _uuid 设备uuid，传入了则直接按uuid查，否则按imei查
     * @return 原始数据+ts+在线状态
     */

    public Result getLatestTimeseries(String imei,String token,String _uuid){
        // 1. 调用Tb历史接口，获取原始数据
        Map<String,Object> resultMap=new HashMap<>();
        resultMap.put("imei",imei);
        if(imei==null){
            imei=DeviceUtil.getKey(_uuid);
            resultMap.put("imei",imei);
        }

        String uuid=null;
        if(_uuid!=null){
            uuid=_uuid;
        }
        else {
            uuid=deviceService.getUUIDByImei(imei);
        }
        Result result=timeseriesService.getTimeseriesHis(uuid,token,null);

        if(!result.getSuccess()){
            resultMap.put("error","error");
            resultMap.put("ErrorMsg",result.getMsg());
            return new Result(FAIL.code,false,resultMap,"Getting data failed!");
        }
        JSONObject jsonObject=(JSONObject) result.getData();

        // 2. 遍历每一个字段，将字段名和value添加到Map，并获取B1帧时间(一定是比较大的ts，因为B1一定比A1后发)

        long tsB1=0L;  // 用于保存其中最大的时间戳，即B1的时间戳
        for(String key:jsonObject.keySet()){
            JSONArray jsonArray=(JSONArray)jsonObject.get(key);  // 获取对应的array
            JSONObject latestValue=jsonArray.getJSONObject(0);  // 只有一个，最新数据
            // value添加到结果里
            resultMap.put(key,latestValue.get("value"));
            // 时间戳取最大的
            long ts=(Long) latestValue.get("ts");
            tsB1=Math.max(tsB1,ts);
        }
        // 3.添加在线状态，将B1帧ts保存为Life_ts便于计算在线离线状态
        resultMap.put("Life_ts",String.valueOf(tsB1));
        // 判断在线状态需要处理以下字段Life_ts、firstError、secondError、minCellVoltage
        Integer onOffStatus=null;
        log.info("firstError:{},secondError:{},minCellVoltage:({}",resultMap.get("firstError"),resultMap.get("secondError"),resultMap.get("minCellVoltage"));
        if(resultMap.get("firstError")!=null && resultMap.get("secondError")!=null && resultMap.get("minCellVoltage")!=null){
            // 暂时保存这三个字段
            Object firstError=resultMap.get("firstError");
            Object secondError=resultMap.get("secondError");
            Object minCellVoltage=resultMap.get("minCellVoltage");
            // 进行预处理并覆盖原值，便于计算在线状态
            resultMap.put("firstError",TimeseriesService.dealError(firstError.toString()));
            resultMap.put("secondError",TimeseriesService.dealError(secondError.toString()));
            resultMap.put("minCellVoltage",Integer.parseInt(TimeseriesService.dealMinCellVoltage(minCellVoltage.toString())));
            // 计算在线状态
            System.out.println("onOffStatus imei:"+imei);
            onOffStatus=OnOffStatusService.getOnOffStatus(resultMap,imei);
            // 计算完毕后恢复原值
            resultMap.put("firstError",firstError);
            resultMap.put("secondError",secondError);
            resultMap.put("minCellVoltage",minCellVoltage);
        }

        resultMap.put("onOffStatus",onOffStatus);

        // 4.添加时间戳，移除Life_ts，修改key为tsB1
        resultMap.remove("Life_ts");
        resultMap.put("tsB1",tsB1);


        return new Result(SUCCESS.code, true, resultMap, "success");
    }

    /**
     * 按页查询设备最新数据
     * @param pageSize 页大小
     * @param page 页码，从0开始
     * @param textSearch 过滤关键词
     * @param customerTitle 项目号，不传的情况查所有设备，传的话只查该项目号下的设备
     * @param token 鉴权token
     * @return 按页返回设备最新数据
     */
    public Result getLatestTimeriesBatch(Integer pageSize, Integer page,String textSearch,String customerTitle,String token){
        // 1. 查询设备列表
        Result getDeviceRes=getDevices(pageSize,page,textSearch,customerTitle,token);
        if(!getDeviceRes.getSuccess()){  // 查询失败
            return getDeviceRes;
        }
        Map<String,Object> resultMap=new HashMap<>();
        // 2.遍历列表查询设备数据
        Map<String,Object> resultMapGetDevice= (Map<String,Object>) getDeviceRes.getData();  // 查询设备方法返回result的data部分，其resultMap
        Map<String,String> deviceImeiUUIDMap=(Map<String,String>)resultMapGetDevice.get("deviceImeiUUIDMap");  // 获取其中的设备列表
        Map<String,Long> deviceImeiCreatedTimeMap=(Map<String,Long>)resultMapGetDevice.get("deviceImeiCreatedTimeMap");
        Map<String,String> deviceImeiCustomerTitleMap=(Map<String,String>)resultMapGetDevice.get("deviceImeiCustomerTitleMap");


        List<Object> dataList=new LinkedList<>();

        // =========此处应该使用多线程进行查询
        for(String deviceImei:deviceImeiUUIDMap.keySet()){
            String deviceUUID=deviceImeiUUIDMap.get(deviceImei);
            Result resultDevice=getLatestTimeseries(deviceImei,token,deviceUUID);  // 通过uuid查询设备最新数据
            // 添加创建时间和项目号
            Map<String,Object> resultDeviceMap=(Map<String,Object>)resultDevice.getData();  // 不管成功与否都是返回resultMap
            resultDeviceMap.put("uuid",deviceUUID);
            resultDeviceMap.put("createdTime",deviceImeiCreatedTimeMap.get(deviceImei));
            resultDeviceMap.put("customerTitle",deviceImeiCustomerTitleMap.get(deviceImei));
            dataList.add(resultDeviceMap);  // 添加到列表

        }
        //========↑
        // 3.构造结果map,放入设备信息以及其他杂项
        resultMap.put("devices",dataList);
        // 放入其他杂项
        resultMap.put("totalPages",resultMapGetDevice.get("totalPages"));
        resultMap.put("totalElements",resultMapGetDevice.get("totalElements"));
        resultMap.put("hasNext",resultMapGetDevice.get("hasNext"));
        resultMap.put("pageSize",pageSize);
        resultMap.put("page",page);



        return new Result(SUCCESS.code,true,resultMap,"success");
    }

    public Result getTimeseries(String imei,String keys,Long startTs,Long endTs, String token, String _uuid){
        // 1. 调用Tb接口获取该时间内的数据
        Map<String,Object> resultMap=new HashMap<>();
        String uuid=null;
        if(_uuid!=null){  // 传uuid只是在已经查过uuid的情况下方便不用重新查一遍，imei必须要传
            uuid=_uuid;
        }
        else {
            uuid=deviceService.getUUIDByImei(imei);
        }
        String params=null;
        if(keys!=null){
            params="?"+"keys="+keys;
        }
        // 只有传入keys的情况startTs才会生效
        if(startTs!=null && params!=null){
            params = params + "&"+"startTs="+startTs;
        }
        if(endTs !=null && params!=null){
            params = params + "&"+"endTs="+endTs;
        }
        log.info("getHisTimeseries, uuid:{}, params:{}",uuid,params);
        Result result=timeseriesService.getTimeseriesHis(uuid,token,params);

        if(!result.getSuccess()){
            // 构造一个List<Map>来存放error信息，保证和正常情况返回相同格式的数据便于后续处理
            List<Map<String,Object>> dataList=new LinkedList<>();
            Map<String,Object> errorMap=new HashMap<>();
            errorMap.put("error","error");
            errorMap.put("ErrorMsg",result.getMsg());
            dataList.add(errorMap);

            resultMap.put(imei,dataList);
            return new Result(FAIL.code,false,resultMap,"Getting data failed!");
        }

        // 整个object，包括所有字段，{key1:[{ts,value},{ts,value}], key2:[{ts,value},{ts,value}]}
        JSONObject jsonObject=(JSONObject) result.getData();

        // 2. 将所有数据按ts进行分组，每个ts对应一个对象，其中含有该ts的所有字段的key:value值
        Map<Long, Map<String,Object>> tsMap=new HashMap<>(); // key:ts，value:每一个字段的key:value对

        for(String key:jsonObject.keySet()){
            JSONArray jsonArray=(JSONArray)jsonObject.get(key);  // 获取对应的array key1:[{ts,value},{ts,value}]
            for(int i=0;i<jsonArray.size();i++){  // 遍历其中每一个ts、value对象
                JSONObject object=jsonArray.getJSONObject(i);  // 获取对应的{ts,value}
                long ts=(Long) object.get("ts");

                if(tsMap.containsKey(ts)){  // 已经有这个ts的数据，将当前数据放入即可
                    tsMap.get(ts).put(key,object.get("value"));  // key是当前array对应的字段名，value是当前字段当前ts的值
                } else {  // 没有这个ts的数据，创建一个Map<String,Object>表示这个ts的所有字段的key:value
                    Map<String,Object> dataMap=new HashMap<>();  // 创建初始key:value map
                    dataMap.put(key,object.get("value"));  // 放入字段的当前值
                    dataMap.put("ts",ts); // 放入当前对应的时间戳
                    tsMap.put(ts,dataMap);  // 放入tsMap便于放入下一个该时间戳的字段数据
                }
            }
        }

        // 此时tsMap中含有ts对应的所有字段的key:value值  ts1:{ts1时的B1帧数据1,ts1},ts2:{ts2时的B1帧数据2,ts2}, ... , ts3:{A1帧数据,ts}
        List<Map<String,Object>> dataList=new LinkedList<>();
        for(Long key:tsMap.keySet()){
            dataList.add(tsMap.get(key));
        }
        // 放入结果数组返回
        resultMap.put(imei,dataList);  // imei:[ {ts1时的B1帧数据1,ts1}, {ts2时的B1帧数据2,ts2}, ... , {A1帧数据,ts} ]
        return new Result(SUCCESS.code, true, resultMap, "success");
    }

    public Result getTimeseriesBatch(Integer pageSize, Integer page,String textSearch,String customerTitle,String token,
                                     String keys,Long startTs,Long endTs){
        // 1. 查询设备列表
        Result getDeviceRes=getDevices(pageSize,page,textSearch,customerTitle,token);
        if(!getDeviceRes.getSuccess()){  // 查询失败
            return getDeviceRes;
        }
        Map<String,Object> resultMap=new HashMap<>();
        // 2.遍历列表查询设备数据
        Map<String,Object> resultMapGetDevice= (Map<String,Object>) getDeviceRes.getData();  // 查询设备方法返回result的data部分，其resultMap
        Map<String,String> deviceImeiUUIDMap=(Map<String,String>)resultMapGetDevice.get("deviceImeiUUIDMap");  // 获取其中的设备列表
        Map<String,Long> deviceImeiCreatedTimeMap=(Map<String,Long>)resultMapGetDevice.get("deviceImeiCreatedTimeMap");
        Map<String,String> deviceImeiCustomerTitleMap=(Map<String,String>)resultMapGetDevice.get("deviceImeiCustomerTitleMap");

        // 存 [ imei:[{B1数据},{A1数据},{杂项数据}], ... ]
        List<Map.Entry<String,Object>> dataList=new LinkedList<>();
        for(String deviceImei:deviceImeiUUIDMap.keySet()){
            // 构造杂项数据对象
            Map<String,Object> dataRemain=new HashMap<>();
            String deviceUUID=deviceImeiUUIDMap.get(deviceImei);
            dataRemain.put("uuid",deviceUUID);
            dataRemain.put("createdTime",deviceImeiCreatedTimeMap.get(deviceImei));
            dataRemain.put("customerTitle",deviceImeiCustomerTitleMap.get(deviceImei));

            // 使用设备imei查询设备该时间段的数据
            // getTimeseries(String imei,String keys,Long startTs,Long endTs, String token, String _uuid)
            Result resultDevice=getTimeseries(deviceImei,keys,startTs,endTs,token,deviceUUID);
            // { imei:[ {ts1时的B1帧数据1,ts1}, {ts2时的B1帧数据2,ts2}, ... , {A1帧数据,ts} ]}
            Map<String,Object> resultDeviceMap=(Map<String,Object>)resultDevice.getData();
            // [ {ts1时的B1帧数据1,ts1}, {ts2时的B1帧数据2,ts2}, ... , {A1帧数据,ts} ]
            List<Map<String,Object>> resultDeviceList=(List<Map<String,Object>>) resultDeviceMap.get(deviceImei);
            // [ {ts1时的B1帧数据1,ts1}, {ts2时的B1帧数据2,ts2}, ... , {A1帧数据,ts}, {uuid,createdTime,customerTitle} ]
            resultDeviceList.add(dataRemain);// 获取数据列表并添加杂项数据

            Map.Entry<String,Object> entry = new AbstractMap.SimpleEntry<String, Object>(deviceImei, resultDeviceList);
            dataList.add(entry);
        }
        // 3.构造结果map,放入设备信息以及其他杂项
        resultMap.put("devices",dataList);
        // 放入其他杂项
        resultMap.put("totalPages",resultMapGetDevice.get("totalPages"));
        resultMap.put("totalElements",resultMapGetDevice.get("totalElements"));
        resultMap.put("hasNext",resultMapGetDevice.get("hasNext"));
        resultMap.put("pageSize",pageSize);
        resultMap.put("page",page);
        return new Result(SUCCESS.code,true,resultMap,"success");
    }


    /**
     * 按页查询设备列表
     * @param pageSize 页大小
     * @param page 页数，从0开始
     * @param textSearch 过滤关键词
     * @param customerTitle 项目号，不传的情况查所有设备，传的话只查该项目号下的设备
     * @param token 鉴权token
     * @return 三个map 设备imei:uuid，设备imei:创建时间，设备imei:项目号
     */

    private Result getDevices(Integer pageSize, Integer page,String textSearch,String customerTitle,String token){
        // 1. 首先构造按租户查设备的url，即查所有设备的url
        String params="?"+"pageSize="+pageSize+"&"+"page="+ page +"&"+"type="+TYPE;  // 参数
        String url=apiConfig.getDeviceInfosApi()+params;  // 查所有设备列表

        // 2.传入了项目号，只查该项目号对应customer的设备，用该customerId构造查询设备列表的url，更新上述url
        if(customerTitle!=null){  // 传入了项目号，只查该customerId下的设备
            // 2.1. 根据项目号查询customerId
            String urlGetCustomerId=apiConfig.getIdsByCustomerTitle()+"?"+"customerTitle="+customerTitle;  // 构造查询customerId的url
            log.info("项目号查询customerId，url：{}",urlGetCustomerId);
            String[] resultStr= HttpClientUtil.get_data(urlGetCustomerId,token);  // 查询customerId
            if(Integer.parseInt(resultStr[0])!=200){//没有正确拿到信息
                log.error("GetCustomerId ERROR: code:"+resultStr[0]+" result:"+resultStr[1]);
                try{//错误但是返回json格式
                    //尝试拿数据
                    JSONObject jsonResult = JSONObject.parseObject(resultStr[1]);
                    log.info(jsonResult.toString());
                    String msg=jsonResult.get("message").toString();
                    log.error(msg);
                    return new Result(FAIL.code,false,null,msg);
                } catch (Exception e){//错误而且返回的不是JSON格式
                    log.error("msg: "+resultStr[1]);
                    return new Result(FAIL.code,false,null,resultStr[1]);
                }

            } else{
                // 2.2 获取customerId
                JSONObject jsonResult = JSONObject.parseObject(resultStr[1]);
                String customerId=jsonResult.getJSONObject("id").get("id").toString();
                // 2.3 根据查询到的customerId构造url
                url=apiConfig.getCustomerDeviceInfosApiPrefix()+customerId+apiConfig.getCustomerDeviceInfosApiSuffix()+params;
            }

        }
        if(textSearch!=null){
            url=url+"&"+"textSearch="+textSearch;
        }
        log.info("查询设备列表, url:{}",url);
        String[] resultStr= HttpClientUtil.get_data(url,token);
        if(Integer.parseInt(resultStr[0])!=200){//没有正确拿到信息
            log.error("GetDeviceERROR: code:"+resultStr[0]+" result:"+resultStr[1]);
            try{//错误但是返回json格式
                //尝试拿数据
                JSONObject jsonResult = JSONObject.parseObject(resultStr[1]);
                log.info(jsonResult.toString());
                String msg=jsonResult.get("message").toString();
                log.error(msg);
                return new Result(FAIL.code,false,null,msg);
            } catch (Exception e){//错误而且返回的不是JSON格式
                log.error("msg: "+resultStr[1]);
                return new Result(FAIL.code,false,null,resultStr[1]);
            }

        } else{
            Map<String,Object> resultMap=new HashMap<>();

            // 获取设备列表
            JSONObject jsonResult = JSONObject.parseObject(resultStr[1]);
            JSONArray dataList=jsonResult.getJSONArray("data");  // 获取设备数据列表

            Map<String,String> deviceImeiUUIDMap=new HashMap<>();  // 设备imei-uuid
            Map<String,Long> deviceImeiCreatedTimeMap=new HashMap<>();  // imei-创建时间
            Map<String,String> deviceImeiCustomerTitleMap=new HashMap<>();  // imei-项目号

            for(int i=0;i<dataList.size();i++){
                // 设备imei-uuid
                deviceImeiUUIDMap.put(dataList.getJSONObject(i).get("name").toString(),  // imei号
                        dataList.getJSONObject(i).getJSONObject("id").get("id").toString());  // uuid
                // imei号-创建时间createdTime
                if(dataList.getJSONObject(i).get("createdTime")!=null){
                    deviceImeiCreatedTimeMap.put(dataList.getJSONObject(i).get("name").toString(),  // imei号
                            (Long)dataList.getJSONObject(i).get("createdTime"));  // 创建时间
                }
                // imei-项目号customerTitle
                if(dataList.getJSONObject(i).get("customerTitle")!=null){
                    deviceImeiCustomerTitleMap.put(dataList.getJSONObject(i).get("name").toString(),  // imei号
                            dataList.getJSONObject(i).get("customerTitle").toString());  // 项目号
                }

            }
            resultMap.put("deviceImeiUUIDMap",deviceImeiUUIDMap);  // 设备-uuid列表
            resultMap.put("deviceImeiCreatedTimeMap",deviceImeiCreatedTimeMap);  // 设备-创建时间列表
            resultMap.put("deviceImeiCustomerTitleMap",deviceImeiCustomerTitleMap);  // 设备-项目号列表
            resultMap.put("totalPages",jsonResult.get("totalPages"));  // 总页数
            resultMap.put("totalElements",jsonResult.get("totalElements"));  // 总设备数
            resultMap.put("hasNext",jsonResult.get("hasNext"));  // 是否有下一页

            return new Result(SUCCESS.code, true, resultMap, "success");
        }
    }

    public Result getAlarmData(String imei,String token){
        // =====1. 查询故障类型
        String url=makeUrlByImei(imei,null);
        if(url==null){
            return new Result(FAIL.code, false, null, "Invalid UUID string: null");
        }


        // 查询和故障有关的所有字段

        // 筛选存在故障的字段

        return null;
    }

    public Result getLatestTimeriesByAlarm(String defaultValue,String token,Integer page,Integer pageSize){
        Map<String,Object> resultMap=new HashMap<>();
        // 1.根据传入的参数构造Filter，FirstError和SecondError要分开
        // FirstError的
        KeyFilter<String> firstErrorKeyFilter=new KeyFilter<>("firsterror_set","TIME_SERIES","STRING","STRING","CONTAINS",defaultValue,null);
        // SecondError的
        KeyFilter<String> secondErrorKeyFilter=new KeyFilter<>("seconderror_set","TIME_SERIES","STRING","STRING","CONTAINS",defaultValue,null);


        // 2.按页多次发送请求获取完整列表（直接存uuid），完整的RequestBody在
        Set<String> deviceSet=new HashSet<>();
        List<KeyFilter<?>> keyFilterList=new ArrayList<>();  // 接口过滤器参数
        // 2.1 FirstError的
        keyFilterList.add(firstErrorKeyFilter);
        Result firstErrorResult=getAlarmDevices(keyFilterList,token);  // 查询设备列表
        if(!firstErrorResult.getSuccess()){  // 查询失败
            return firstErrorResult;
        }
        deviceSet.addAll((Set<String>) firstErrorResult.getData());
        // 3.1 SecondError的
        keyFilterList.clear();  // 清空filterList重新查secondError的

        keyFilterList.add(secondErrorKeyFilter);
        Result secondErrorResult=getAlarmDevices(keyFilterList,token);  // 查询设备列表
        if(!secondErrorResult.getSuccess()){  // 查询失败
            return secondErrorResult;
        }
        deviceSet.addAll((Set<String>) secondErrorResult.getData());
        if(deviceSet.size()==0){  // 没有该报警
            resultMap.put("data",deviceSet);
            resultMap.put("totalPages",0);
            resultMap.put("pageSize",pageSize);
            resultMap.put("hasNext",false);
            resultMap.put("page",page);
            resultMap.put("totalElements",0);
            return new Result(SUCCESS.code,true,resultMap,"success");
        }
        // 此时Set<String> deviceSet中为所有要查询的设备
        // 3.排序，并分页选出应该查询的设备
        List<String> deviceList = new ArrayList<>(deviceSet);  // 转化为List
        Collections.sort(deviceList);  // 排序
        // 排序后进行分页，选出子列表
        ListPage<String> pagedList=PageUtil.getListPaged(deviceList,page, pageSize);

        // 4. 根据列表依次查询（多线程）
        // =======================多线程实现=========================
//        List<Object> resultList=getAlarmResultMultiThread(deviceList,token);
        // ======================非多线程实现=========================
        List<Object> resultList=getAlarmResultNormal(pagedList.getSubList(),token);

        // 5. 保存结果
        resultMap.put("data",resultList);
        resultMap.put("totalPages",pagedList.getTotalPages());
        resultMap.put("pageSize",pagedList.getPageSize());
        resultMap.put("hasNext",pagedList.getHasNext());
        resultMap.put("page",pagedList.getPage());
        resultMap.put("totalElements",pagedList.getTotalElements());


        log.info("firstErrorDeviceNum:{},SecondErrorDeviceNum:{},totalDeviceNum:{}, setToListSize:{}",((Set<String>) firstErrorResult.getData()).size(),((Set<String>) secondErrorResult.getData()).size(),deviceSet.size(),deviceList.size());
        return new Result(SUCCESS.code,true,resultMap,"success");
    }
    // 收集存在报警的设备uuid
    private  Result getAlarmDevices(List<KeyFilter<?>> keyFilters, String token){
        // 1. 获取请求体框架
        ClassPathResource resource = new ClassPathResource("entityQuery.json");
        InputStream inputStream = null;
        try {
            inputStream = resource.getInputStream();
        } catch (Exception e) {
            log.error("Cannot get json file! Exception:",e);
            return new Result(FAIL.code,false,null,"Cannot get json file! Exception:");
        }
        String jsonString = JsonUtil.readJsonFile(inputStream);
        JSONObject jsonObject= JSON.parseObject(jsonString);
        // 必需的两个参数判断
        if(jsonObject.getJSONArray("keyFilters")==null){
            log.error("KeyFilters doesn't exist, json:{}",jsonObject.toString());
            return new Result(FAIL.code,false,null,"KeyFilters doesn't exist");
        }
        if(jsonObject.get("pageLink")==null || jsonObject.getJSONObject("pageLink").getInteger("page")==null){
            log.error("PageLink doesn't exist, json:{}",jsonObject.toString());
            return new Result(FAIL.code,false,null,"PageLink doesn't exist");
        }


        // 2.将KeyFilters添加到请求体框架，从而构造完整的请求体
        for (KeyFilter<?> keyFilter : keyFilters) {
            // 添加到keyFilters
            jsonObject.getJSONArray("keyFilters").add(JSONObject.toJSON(keyFilter));
        }

        // 3. 按页依次查询结果
        Set<String> devices=new HashSet<>();
        String url=apiConfig.entityQueryApi();
        // 3.1第一次查询
        String[] resultStr=HttpClientUtil.post_data(url,jsonObject,token);
        int totalPages=0;
        int page=jsonObject.getJSONObject("pageLink").getInteger("page");
        System.out.println("start page:"+jsonObject.getJSONObject("pageLink").getInteger("page"));
        // 3.2循环按页查询
        while(true){
            if(Integer.parseInt(resultStr[0])!=200){//没有正确拿到信息
                log.error("Get Alarm Device ERROR: code:"+resultStr[0]+" result:"+resultStr[1]);
                try{//错误但是返回json格式
                    //尝试拿数据
                    JSONObject jsonResult = JSONObject.parseObject(resultStr[1]);
                    log.info(jsonResult.toString());
                    String msg=jsonResult.get("message").toString();
                    log.error(msg);
                    return new Result(FAIL.code,false,null,msg);
                } catch (Exception e){//错误而且返回的不是JSON格式
                    log.error("msg: "+resultStr[1]);
                    return new Result(FAIL.code,false,null,resultStr[1]);
                }
            }
            System.out.println("page"+page);
            totalPages++;
            // 3.2.1 获取设备列表
            JSONObject jsonResult = JSONObject.parseObject(resultStr[1]);
            JSONArray dataList=jsonResult.getJSONArray("data");
            // 将所有设备添加到结果Set
            for(int i=0;i<dataList.size();i++){
                // 打印imei:uuid信息
                System.out.println("alarm device "
                        +dataList.getJSONObject(i).getJSONObject("latest").getJSONObject("ENTITY_FIELD").getJSONObject("name").get("value")+":"
                        +dataList.getJSONObject(i).getJSONObject("entityId").get("id"));
                if(dataList.getJSONObject(i).getJSONObject("entityId").get("id")!=null){
                    devices.add(dataList.getJSONObject(i).getJSONObject("entityId").get("id").toString());
                }
            }

            // 3.2.2判断是否有还有下一页
            if(!Boolean.parseBoolean(jsonResult.get("hasNext").toString())){//最后一页
                System.out.println("Alarm最后一页啦，page:"+page+" totalPages:"+totalPages);
                break;
            }
            // 修改请求体中pageLink的当前页page的值，url不变
            page++;
            jsonObject.getJSONObject("pageLink").put("page",page);
            System.out.println("current page:"+jsonObject.getJSONObject("pageLink").getInteger("page"));
            resultStr=HttpClientUtil.post_data(url,jsonObject,token);//String 格式获取结果, [0]为statusCode，[1]为结果(结果可能不是json)
            if(resultStr==null || Integer.parseInt(resultStr[0])!=200){
                log.error("Cannot get Alarm device, resultStr:{}",resultStr);
            }
        }
        log.info("alarm deviceNum:{}",devices.size());  // 打印设备数量信息
        return new Result(SUCCESS.code,true,devices,"success");
    }
    // 非多线程调用getLatestTimeseries()查询设备数据
    private List<Object> getAlarmResultNormal(List<String> deviceSet,String token){
        List<Object> resultList=new LinkedList<>();
        for(String uuid:deviceSet){
            // 1. 获取customerTitle
            String customerTitle=DeviceUtil.uuidCustomerTitle.get(uuid);
            // 2. 获取最新数据
            Result result=getLatestTimeseries(null,token,uuid);
            // 3-1. 未成功查询到数据，此时结果列表中应该只含imei和customerTitle
            if(result.getData()==null){
                Map<String,Object> nothing=new HashMap<>();
                nothing.put("imei",DeviceUtil.getKey(uuid));
                nothing.put("customerTitle",customerTitle);
                resultList.add(nothing);
                continue;
            }
            // 3-2 成功查到数据则将数据加入customerTitle加入结果列表
            Map<String,Object> resultData=(Map<String,Object>)result.getData();
            resultData.put("customerTitle",customerTitle);
//            resultList.add(result.getData());
            resultList.add(resultData);
        }
        return resultList;
    }
    // 多线程调用getLatestTimeseries()查询设备数据
    // todo 结果添加customerTitle（非多线程实现已完成）
    private List<Object> getAlarmResultMultiThread(List<String> deviceSet,String token){
        // 创建线程池
        int corePoolSize=Runtime.getRuntime().availableProcessors(); // 获取核心线程数，使用机器处理器核心数量
        ExecutorService executorService = Executors.newFixedThreadPool(2*corePoolSize);  // 线程池大小设置为核心线程数的两倍
//        ExecutorService executorService = Executors.newCachedThreadPool();  // 动态大小

        // 创建一个列表来保存所有任务的结果。
        List<Future<Result>> futureList = new LinkedList<>();

        // 遍历需要查询的数据，为每个数据创建一个任务，并将任务提交给线程池执行。
        for(String uuid: deviceSet){
            Future<Result> future = executorService.submit(new QueryAlarmTask(uuid, token));  // 提交任务查询数据
            futureList.add(future);  // 结果添加到列表
        }

        List<Object> resultList=new LinkedList<>();

        // 等待所有任务执行完成，并收集结果。
        for (Future<Result> future : futureList) {
            try {
//                System.out.println(future);
                Result result = future.get(); // 阻塞等待任务执行完成并获取结果
                resultList.add(result.getData());  // 获取查询结果
            } catch (InterruptedException | ExecutionException e) {
                // 处理异常
                log.error("MultiThread Exception! ",e);

            }
        }
        executorService.shutdown();
        return resultList;
    }


    /**
     * 辅助方法，通过imei或uuid构建url
     */
    // 通过imei构造查询url
    private String makeUrlByImei(String imei,String keys){
        String params=null;
        if(keys!=null && !keys.isEmpty()){
            params="?keys="+keys;
        }
        String uuid= deviceService.getUUIDByImei(imei);
        if(uuid==null){
            return null;
        }
        return makeUrlByUUID(uuid,params);

    }
    // 通过uuid构造查询url
    private String makeUrlByUUID(String uuid,String params){
        if(uuid==null){
            return null;
        }
        String url=apiConfig.getDataApiPrefix()+uuid+apiConfig.getDataApiSuffix();
        if(params!=null&&!params.isEmpty()){  // 有参数
            url=url+params;
        }
        return url;
    }

    public static void main(String[] args) {
        // 测试查询报警设备列表是否正确
        FrameServiceImpl frameService=new FrameServiceImpl();
        List<KeyFilter<?>> keyFilters=new LinkedList<>();
        String token="eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJ0ZW5hbnRAdGhpbmdzYm9hcmQub3JnIiwidXNlcklkIjoiNWQ1Yzk3YTAtYzk5Ni0xMWVkLThhZjctYzVmMjQ5NTkxMDliIiwic2NvcGVzIjpbIlRFTkFOVF9BRE1JTiJdLCJzZXNzaW9uSWQiOiI1ZmU0ZDEzYi1jZDc2LTQ5YjQtYTQyMC1kNDE3M2E2YWMzZTMiLCJpc3MiOiJ0aGluZ3Nib2FyZC5pbyIsImlhdCI6MTcwNTU2MjY2NCwiZXhwIjoxNzA1NTcxNjY0LCJlbmFibGVkIjp0cnVlLCJpc1B1YmxpYyI6ZmFsc2UsInRlbmFudElkIjoiNWNkYzZiMjAtYzk5Ni0xMWVkLThhZjctYzVmMjQ5NTkxMDliIiwiY3VzdG9tZXJJZCI6IjEzODE0MDAwLTFkZDItMTFiMi04MDgwLTgwODA4MDgwODA4MCJ9.NOGhghVwZM_0mGlUJcvFa054TXvg12BSPgJ2pxhjko68N6zVPqrTtlqW8enqgG8uMUw2oO1XDv4ckGY06aeSNw";

        KeyFilter<String> keyFilter=new KeyFilter<>("firstError","TIME_SERIES","STRING","STRING","EQUAL","N00000000",null);
        keyFilters.add(keyFilter);
        Result result=frameService.getAlarmDevices(keyFilters,token);
        if(!result.getSuccess()){
            System.out.println(result.toString());
        }
        Set<String> devices=(Set<String>)result.getData();
        for(String device:devices){
            System.out.println(device);
        }
        System.out.println("DeviceNum:"+devices.size());

    }





}

class QueryAlarmTask implements Callable<Result> {
    private String uuid;
    private String token;

    private FrameServiceImpl frameService=new FrameServiceImpl();

    QueryAlarmTask(String uuid, String token) {
        this.uuid = uuid;
        this.token = token;
    }

    @Override
    public Result call() throws Exception {
        Result result=frameService.getLatestTimeseries(null,token,uuid);
//        System.out.println("call result:"+result.toString());
        return result;  // 使用最新数据接口查询数据
    }
}

